Игра Сапёр на поле 9 на 9.
Как обычно, открыть все ячейки, стараясь не открыть ячейки с минами.
Квадратное поле 9 x 9 (1…9 x 1…9). Количество мин задаётся в начале
игры. Что считать координатой X, а что Y, решайте сами, главное,
чтобы быть в этом последовательным. Как и в начальных версиях игры под Windows
мины расставляются до первого хода. Поэтому подорваться
можно уже первым ходом.
Всё поле, чтобы не держать в памяти как
ПМК,
проще рисовать на бумаге. ПМК только выдаёт количество мин вокруг указанной
ячейки после каждого хода.
Перед первым запуском несколько констант в регистры. Если используете загрузку образа в эмулятор, то они уже введены.
Регистр | Значение |
---|---|
R6 | Случайное число. Можно оставить нулевым. |
R7 | 73 |
R9 | 0.11828112 |
Ra | 100 |
Rb | 87 |
Rс | 63 |
Rd | 0.35000033 |
Начало игры: вводим количество мин от 1 до 63. Рекомендую 20. Затем В/О С/П.
ПМК расставит на поле мины случайным образом и по окончании выдаст 3.1415926 (это π).
Вы задаёте координаты клетки, которую хотите проверить, в виде двузначного
числа. От 11 до 99, без нулей!. Как указано выше, сами выбирайте, какой разряд считать
координатой по горизонтали, какой по вертикали. И после С/П
ПМК отобразит на экране количество мин вокруг указанной ячейки. Если высветится
ЕГГ0Г , то это значит, что в указанном месте была мина и
вы подорвались
– игра закончена.
Если вы поняли, где все мины, или подорвались
, или просто не нравится текущая расстановка, то
начало новой игры как указано выше, только никаких вводов значений в регистры
не потребуется. В связи с тем, что генератор случайных чисел
постоянно меняет базовое значение, в новой игре будет другая расстановка.
Программа достаточно медленная, поэтому если используете эмулятор ПМК с ускорением, то включите ускорение. Приятной игры!
В отличие от пользователя, ПМК использует поле не 9 x 9, а 11 x 11. Получаются координаты от 00 до 110, где последняя цифра (для определённости) – это номер по горизонтали.
Лучше показать поле в виде таблицы с числовыми координатами:
100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 |
90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 |
80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 |
70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 |
50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 |
В данном случае серая зона по краям – это виртуальные координаты, которые знает ПМК, но не видит пользователь. Обратите внимание, что самый правый ряд – это копия самого левого. При линейном представлении справа от 19 идёт 20, но слева от 21 – тоже 20.
ПМК для распределения мин использует только внутреннее поле, но хранит и в расчётах использует всю таблицу. Это позволяет держать границы поля свободными от мин, что упрощает функции подсчёта.
Что касается обхода
, то это лучше увидеть на центральном голубом квадрате.
Для примера, пусть пользователь указал координаты 55. В этом случае нужно
посчитать мины в ячейках квадрата вокруг него, т. е. в координатах
44, 45, 46, 54, 56, 64, 65, 66.
В общем случае для любой ячейки с координатами P квадрат обхода
–
это ячейки с координатами P − 11, P − 10, P − 9,
P − 1, P + 1, P + 9, P + 10, P + 11.
Вот для чего введены виртуальные границы. Чтобы на краях не заниматься
сложными расчётами, потому что в виртуальных ячейках всегда нет мин.
Теперь технические детали. У ПМК нет столько регистров памяти, чтобы хранить 111 чисел. Поэтому он для каждой ячейки использует один бит. Для операций с битами используются шестнадцатеричные представления чисел. Т. к. одна шестнадцатеричная цифра может представлять 4 бита, то используя 7 цифр, который максимум в шестнадцатеричных операциях, получаем 7 × 4 = 28. Итого, если использовать 4 регистра памяти, получается 4 × 28 = 112. Как раз хватит для нашего случая.
Второй момент – это вычисление координат обхода
. Для удобства формула
выше для вычисления ячеек обхода
с произвольной координаты P хранится
в относительном виде. К начальному P − 11 потом добавляется:
+1, +1, +8, +2, +8, +1, +1. А это как раз составляет содержимое загадочной константа
из R9 (0.11828112). В ней последняя цифра может быть произвольной не нулевой, потому
что используется только как флаг окончания цикла обхода
. Но задана
явно как два, чтобы использовать эту же константу для косвенного перехода на адрес 12.
R0 | Рабочий регистр. Используется для циклов. |
---|---|
R1, R2, R4, R8 | Битовые поля мин. |
R3 | Координаты текущей мины. |
R5 | Счётчик мин. |
R6 | Случайное число. |
R7 | 73. Адрес процедуры проверки наличия в клетке с координатами из R3 мины. |
R9 | Константа 0.11828112 для обхода. А хвостик – это адрес начала цикла расстановки мин. |
Ra | 100. Кроме умножения и вычитания это адрес переход на 00. |
Rb | 87. Адрес процедуры деление на 4 с остатком |
Rc | 63. Адрес процедуры проверки координат. |
Rd | Константа 0.35000033, для вычисления 2x. А хвостик – это адрес начала основного цикла программы. |
Re | Индекс регистра поля текущей мины. |
# | | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 |
---|---|---|---|---|---|---|---|---|---|---|
00 | | К[x] | x→П0 | FLg | П→xc | П→x0 | − | F√ | 0 | x→П1 | x→П2 |
10 | | x→П4 | x→П8 | П→x6 | 1 | 1 | × | Fπ | + | К{x} | x→П6 |
20 | | П→xa | × | КППc | Кx<09 | КПП7 | Кx=09 | x→П3 | + | КП→xe | К∨ |
30 | | Кx→Пe | FL0 | 12 | Fπ | С/П | Кx≥0d | КППc | Кx<0d | П→x9 | x→П0 |
40 | | КПП7 | Кx=0a | x→П5 | 1 | 1 | − | П→x3 | + | x→П3 | КПП7 |
50 | | Fx≠0 | 53 | КП→x5 | П→x0 | ВП | 1 | В↑ | К{x} | x→П0 | Fx=0 |
60 | | 45 | П→x5 | БП | 34 | x→П3 | КИНВ | КИНВ | К{x} | П→x3 | П→xa |
70 | | − | × | В/О | П→x3 | КППb | x→Пe | <-> | КППb | <-> | 1 |
80 | | + | F10x | + | КП→xe | К∧ | К{x} | В/О | 4 | ÷ | К[x] |
90 | | FВx | К{x} | П→xd | ÷ | Fex | К[x] | В/О |
Поняв основные идеи, перейдём к программе. В целом программу можно представить так:
Сначала рассмотрим вспомогательные процедуры.
Число содержит | Если верно | Если нет |
---|---|---|
Минимум две ненулевых цифры | Результат > 0 | Ноль |
Максимум две цифры | Результат < 0 | ≥ 0 |
правильноечисло из двух ненулевых цифр. Первая проверка использует тот факт, что команда КИНВ первую ненулевую цифру (или единственный ноль) превращает в 8. А в дробной части будут содержаться остальные ненулевые цифры. Команду КИНВ приходится делать дважды, чтобы избежать пустышек на месте дробной части. Вторая проверка просто вычитает из числа 100.
по хвосту.
проходитпроцедуру деления №2. Затем результат из множества [1, 2, 4, 8] используется как номер регистра. А целая часть из RY [0…27] снова
проходитпроцедуру деления №2. Целая часть из регистра Y используется для определения разряда бита: 10 в степени [0…6] + 1, а число [1, 2, 4, 8] складывается с числом 10(номер разряда) и получается бит для тестирования, что и делается с помощью операции К∧. Кстати, сам бит остаётся в RY, что позволит его же использовать для последующей установки. Благодаря К{x} возвращается или ноль – нет мин, или не ноль – есть мина. В последнем случае важно, что число меньше единицы. Это пригодится в другом месте.
Теперь можно рассмотреть всю программу.
Адреса 00..06. Ввод числа мин с проверкой. Сохраняется счётчик количества мин в R0 и делается его проверка на ⩾ 1 через FLg, и на ⩽ 63, используя разницу с Rс и затем F√.
Адреса 07..11. Обнуление регистров битовых полей для хранения положения мин.
Адреса 12..19. Генерация случайного числа. Формула последовательности для генерации случайного числа Ni+1 = {Ni × 11 + π}.
Адреса 20..23. Преобразование в координату. Случайное число умножается на 100 и проверяется, что оно состоит их двух ненулевых цифр. Если не так, то возвращается на начало цикла (адрес 12) для генерации другого числа.
Адреса 23..25. Проверка дубликата мины. Если уже есть в этом месте, то снова на начало цикла.
Адреса 26..27. Обнуления текущей координаты, чтобы пользователь не подглядел случайно и удаление этого нуля из стека с помощью операции сложения, чтобы придвинуть битовую маску.
Адреса 28..30. Установка бита для новой мины.
Адреса 31..32. Цикл по числу мин.
Адреса 23..34. Вывод флага окончания инициализации или неудачного выбора координаты для проверки, и остановка для ожидания хода пользователя. Адрес этого фрагмента кода хранится в Rd.
Адреса 35..37. Проверка ввода пользователя. После простой проверки на ≥ 0, что важно для процедуры проверки из Rc, последняя вызывается, с сохранение ввода в R3. Если введена неправильная координата, то переход на начало цикла (Rd).
Адреса 38…42. Подготовка цикла обхода.
Константа для обхода из R9 сохраняется в R0. Делается проверка
координаты пользователя (R3) процедурой из R7. Если результат удачный (ноль),
то этот ноль инициализирует счётчик числа мин вокруг (R5).
Стоит пояснить случай не ноль – нарвались на мину.
В этом случае число будет меньше единицы (см. процедуру №3) и произойдёт
переход на начало программы (Ra, адрес=00). А там
FLg
от нуля и выдаст ошибку.
Адреса 43..60. Цикл обхода
с подсчётом количества мин.
Начинается с −11 (почему обычный минус от нуля справа, а не
/-/, будет ясно чуть позже), которое затем
прибавляется к координатам выбранной ячейки.
Делается проверка через процедуру в R7, и если мина найдена,
то промежуточное увеличение счётчика в R5. Затем идёт вычисление прибавки,
которое записано в первом разряде регистра R0. После умножения на 10 и сдвига,
берётся дробная часть (первый разряд отсекается), сохраняется. И если больше
не осталось, то цикл завершается. А если что-то есть, то целое с дробной
частью удачно
переходит на минус по адресу 45, тем самым оставляя
только этот первый разряд, который снова прибавляется к координатам и т. д.
Адреса 61..63. Вывод итога для пользователя. Просто вывод значения счётчика и переход на остановку.
Оставшаяся часть программы уже рассмотрена ранее, когда разбирались подпрограммы.